package cn.org.tpeach.nosql.tools;


import cn.org.tpeach.nosql.constant.ConfigConstant;
import cn.org.tpeach.nosql.constant.PublicConstant;
import cn.org.tpeach.nosql.exception.ServiceException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class ConfigParserUtils {
    private ConfigParserUtils() {}

    private static class SingletonHolder {
        private static final ConfigParserUtils singleton = new ConfigParserUtils();
    }


    public static ConfigParserUtils getInstance() {
        return SingletonHolder.singleton;
    }

    // 默认注释符号
    private final String comments = "#";
    // 组
    private Pattern _section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
    // 数组
    private Pattern _sectionList = Pattern.compile("\\s*\\<([^]]*)\\>\\s*");
    private Pattern _keyValue = Pattern.compile("\\s*([^=]*)=(.*)");
    private final static String format = "%-10s = %s %s\r\n";
    @Getter
    private Map<String, Object> entries = new LinkedHashMap<>();

    private boolean isComment(String line) {
        if (line.startsWith(comments)) {
            return true;
        }
        return false;

    }

    private String getComment(String currentLine) {
        int lastIndex = currentLine.lastIndexOf(comments);
        if (lastIndex != -1) {
            try {
                //如果 #号两边没有被 ''包围则是注释
                String prefix = currentLine.substring(0, lastIndex);
                String suffix = currentLine.substring(lastIndex + 1);
                if (prefix.indexOf("'") == -1 && suffix.lastIndexOf("'") == -1) {
                    return suffix;
                }
            } catch (Exception e) {
            }

        }
        return "";

    }

    /**
     * @param currentLine
     * @return
     */

    private String removeComment(String currentLine) {
        int lastIndex = currentLine.lastIndexOf(comments);
        if (lastIndex != -1) {
            String prefix = currentLine.substring(0, lastIndex);
            String suffix = currentLine.substring(lastIndex + 1);
            if (prefix.indexOf("'") == -1 && suffix.lastIndexOf("'") == -1) {
                currentLine = prefix;
            }
            currentLine = removeApostrophe(currentLine);
        } else {
            currentLine = removeApostrophe(currentLine);
        }
        return currentLine;
    }

    /**
     * @param currentLine
     * @return
     */
    private String removeApostrophe(String currentLine) {
        int indexOf = currentLine.indexOf("'");
        int lastIndexOf = currentLine.lastIndexOf("'");
        if (indexOf != lastIndexOf) {
            currentLine = currentLine.substring(0, indexOf) + currentLine.substring(indexOf + 1, lastIndexOf);
        }
        return currentLine;
    }

    /**
     * 读取配置文件加载到内存中
     * @param path
     * @throws IOException
     */
    public void readConfigFile(String path) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(IOUtil.getFile(path)), "UTF-8"));
        entries.clear();
        String currentLine, currentsection = null, currentList = null, currentComment = null;
        String currentKey, currentValue;
        int listIndex = -1;
        // 读取
        while ((currentLine = br.readLine()) != null) {
            currentLine = currentLine.trim();
            if (isComment(currentLine)) {
                entries.put(StringUtils.simpleRandomUUID(), ConfigMapper.builder().comment(currentLine).build());
            }
            currentComment = getComment(currentLine);
            // 移除注释
            currentLine = removeComment(currentLine).trim();
            if (StringUtils.isNotBlank(currentLine)) {
                // 查找数组
                Matcher m = _sectionList.matcher(currentLine);
                if (m.matches()) {
                    String listKey = m.group(1).trim();
                    if (StringUtils.isBlank(listKey)) {
                        throw new ServiceException("配置文件异常,<>不能为空");
                    }
                    if (listKey.startsWith("/")) {
                        currentList = null;
                        listIndex = -1;
                    } else {
                        currentList = listKey;
                        entries.put(listKey, new LinkedList<Map<String, Map<String, ConfigMapper>>>());
                    }
                    continue;
                }
                // 查找组
                m = _section.matcher(currentLine);
                if (m.matches()) {
                    currentsection = m.group(1).trim();
                    if (currentList != null) {
                        List<Map<String, Map<String, ConfigMapper>>> list = (List<Map<String, Map<String, ConfigMapper>>>) entries.get(currentList);
                        Map<String, Map<String, ConfigMapper>> map = new HashMap<>(1);
                        map.put(currentsection, new LinkedHashMap<>());
                        list.add(map);
                        listIndex++;
                    } else {
                        entries.put(currentsection, new LinkedHashMap<>());
                    }
                    continue;
                }
                if (currentsection != null) {
                    m = _keyValue.matcher(currentLine);
                    if (m.matches()) {
                        currentKey = m.group(1).trim();
                        currentValue = m.group(2).trim();
                        Map<String, ConfigMapper> group;
                        if (currentList != null) {
                            List<Map<String, Map<String, ConfigMapper>>> list = (List<Map<String, Map<String, ConfigMapper>>>) entries.get(currentList);
                            Map<String, Map<String, ConfigMapper>> map = list.get(listIndex);
                            group = map.get(currentsection);
                        } else {
                            group = (Map<String, ConfigMapper>) entries.get(currentsection);
                        }
                        group.put(currentKey, ConfigMapper.builder().comment(currentComment).value(currentValue).build());
                    }
                }
            }
        }
        //读取默认配置
        readAfter(path);

    }

    /**
     * 默认的配置
     * @param path
     * @throws IOException
     */
    private void readAfter(String path) throws IOException {
        boolean isFresh = false;
        InputStream resourceAsStream = ConfigParserUtils.class.getClassLoader().getResourceAsStream("config.json");
        Reader reader = new InputStreamReader(resourceAsStream,"utf-8");
        int ch;
        StringBuilder sb = new StringBuilder();
        while ((ch = reader.read()) != -1) {
            sb.append((char) ch);
        }
        Map<String, Object> defaultConfigMap = GsonUtil.gsonToMaps(sb.toString());
        if(DataStructureUtils.isNotEmpty(defaultConfigMap)){
            for (Map.Entry<String, Object> entry : defaultConfigMap.entrySet()) {
                String section = entry.getKey();//ConnectList
                Object value = entry.getValue();
                //仅做简单层级的解析
                if(value instanceof Map){
                    Map<String,Object> mapper = (Map) value;//Server
                    Iterator<Map.Entry<String, Object>> iterator = mapper.entrySet().iterator();
                    while (iterator.hasNext()){
                        Map.Entry<String, Object> next = iterator.next();
                        if(next.getValue() instanceof Map){
                            ConfigMapper configMapper = GsonUtil.GsonToBean(GsonUtil.gson2String(next.getValue()), ConfigMapper.class);
                            isFresh = safePutMapperData(section,next.getKey(),configMapper.getValue(),configMapper.getComment(),false) || isFresh ;
                        }else if(next.getValue() instanceof  Collection){
                            List<Map<String, Map<String, ConfigMapper>>> list = getListBySectionList(section);
                            if(Objects.isNull(list)){
                                Collection collection  = (Collection) next.getValue();
                                list = new ArrayList<>();
                                isFresh = true;
                                for (Object o : collection) {
                                    if(o instanceof Map){//id
                                        Map<String, Map<String, ConfigMapper>> newMappper= new HashMap<>();
                                        Map<String,Object> map = (Map<String, Object>) o;
                                        Map<String, ConfigMapper> mapperMap = new HashMap<>();
                                        for (Map.Entry<String, Object> objectEntry : map.entrySet()) {
                                            mapperMap.put(objectEntry.getKey(),GsonUtil.GsonToBean(GsonUtil.gson2String(objectEntry.getValue()), ConfigMapper.class));
                                        }
                                        newMappper.put(next.getKey(),mapperMap);
                                        list.add(newMappper);
                                    }
                                }
                                entries.put(section,list);
                            }
                        }
                    }
                }
            }
        }
        if(isFresh){
            flushEntries(path);
        }
    }



    private String parseWrite(Map<String, Object> entries) {
        if (DataStructureUtils.isEmpty(entries)) {
            return "";
        }

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, Object> entry : entries.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof Map) {
                sb.append("[" + key + "]\r\n");
                Map<String, ConfigMapper> mappper = (Map<String, ConfigMapper>) value;
                appendMapper(sb, mappper);
                sb.append("\r\n");
                // 数组
            } else if (value instanceof List) {
                List<Map<String, Map<String, ConfigMapper>>> list = (List<Map<String, Map<String, ConfigMapper>>>) value;
                sb.append("<" + key + ">\r\n");
                boolean flag = true;
                for (Map<String, Map<String, ConfigMapper>> map : list) {
                    if (DataStructureUtils.isNotEmpty(map)) {
                        if(!flag) {
                            sb.append("\r\n");
                        }
                        flag = true;
                        for (Map.Entry<String, Map<String, ConfigMapper>> entryMap : map.entrySet()) {
                            if(!flag) {
                                sb.append("\r\n");
                            }
                            sb.append("[" + entryMap.getKey() + "]\r\n");
                            appendMapper(sb, entryMap.getValue());
                            flag = false;
                        }
                    }
                    flag = false;

                }
                sb.append("</" + key + ">\r\n\r\n");
                // 注释
            } else if (value instanceof ConfigMapper) {
                ConfigMapper v = (ConfigMapper) value;
                sb.append(v.getComment() + "\r\n");
            } else {
                throw new ServiceException("parseWrite异常，发现未知类型" + value.getClass().getName());
            }

        }

        return sb.toString();
    }


    private void appendMapper(StringBuffer sb, Map<String, ConfigMapper> mappper) {
        if (DataStructureUtils.isEmpty(mappper)) {
            return;
        }
        for (Map.Entry<String, ConfigMapper> entry : mappper.entrySet()) {
            String comment =  StringUtils.isBlank(entry.getValue().getComment())?"":comments+entry.getValue().getComment();
            String value = StringUtils.defaultEmptyToString(entry.getValue().getValue());
            if(value.contains("#") || value.contains("'")) {
                value = "'"+value+"'";
            }
            sb.append(String.format(format, entry.getKey(), value ,comment));
        }
    }

    /**
     *
     * @param section
     * @param key
     * @param value
     * @param isNewValue 为true时替换值 false只有原来的值不存在时才会覆盖
     * @return 是否刷新
     */
    public boolean safePutMapperData( String section,String key,String value,String comment,boolean isNewValue){
        Map<String, ConfigMapper> mapper = (Map<String, ConfigMapper>)  getEntries().get(section);
        boolean isFresh = true;
        if(mapper == null){
            mapper = new HashMap<>();
            getEntries().put(section,mapper);
        }
        ConfigMapper configMapper = mapper.get(key);
        if(configMapper == null){
            configMapper = ConfigMapper.builder().build();
        }
        if(isNewValue || StringUtils.isBlank(configMapper.getValue())){
            configMapper.setValue(value);
            if(StringUtils.isNotBlank(comment)){
                configMapper.setComment(comment);
            }
        }else{
            isFresh = false;
        }
        mapper.put(key,configMapper);
        return isFresh;
    }
    public void safePutMapperData( String section,String key,String value,boolean isNewValue){
        safePutMapperData(section,key,value,null,isNewValue);
    }
    public void safePutMapperData( String section,String key,String value){
        safePutMapperData(section,key,value,null,true);
    }

    /**
     * 添加的数据为数组类型
     * @param section
     * @param key
     * @param connConfig
     * @return
     */
    public boolean putList( String section,String key,Map<String, ConfigMapper> connConfig){
        if(DataStructureUtils.isEmpty(connConfig)){
            throw new ServiceException("添加数组配置不能为空");
        }
        List<Map<String, Map<String, ConfigMapper>>> listBySectionList =  getListBySectionList(section);
        if(DataStructureUtils.isEmpty(listBySectionList)) {
            listBySectionList = new LinkedList<>();
        }
        Map<String, Map<String, ConfigMapper>> newMappper = new HashMap<>(1);
        newMappper.put(key, connConfig);
        listBySectionList.add(newMappper);
        entries.put(section,listBySectionList);
        return true;
    }

    public void initList(String section){
        List<Map<String, Map<String, ConfigMapper>>> listBySectionList =  getListBySectionList(section);
        if(DataStructureUtils.isEmpty(listBySectionList)) {
            listBySectionList = new LinkedList<>();
        }
        entries.put(section,listBySectionList);
    }

    public void flushEntries(String path) throws IOException {
        String parseWrite = parseWrite(entries);
        IOUtil.writeConfigFile(parseWrite, IOUtil.getFile(path));
    }

    public List<Map<String, Map<String, ConfigMapper>>> getListBySectionList (String section) {
        Object value = entries.get(section);
        if(value instanceof List) {
            List<Map<String, Map<String, ConfigMapper>>> list = (List<Map<String, Map<String, ConfigMapper>>>) value;
            return list;
        }
        return null;
    }

    public String getString(String section, String key, String defaultvalue) {
        Object value = entries.get(section);
        if (value instanceof Map) {
            return getString((Map<String, ConfigMapper>) value, section, key, defaultvalue);
        }
        return defaultvalue;
    }
    public String getString(Map<String, ConfigMapper> kv,String section, String key, String defaultvalue) {
        if (kv == null || kv.get(key) == null ||StringUtils.isBlank(kv.get(key).getValue())  ) {
            return defaultvalue;
        }
        return kv.get(key).getValue();
    }

    public short getShort(String section, String key, short defaultvalue) {
        Object value = entries.get(section);
        if (value instanceof Map) {
            return getShort((Map<String, ConfigMapper>) value, section, key, defaultvalue);
        }
        return defaultvalue;
    }
    public short getShort(Map<String, ConfigMapper> kv,String section, String key, short defaultvalue) {
        if (kv == null || kv.get(key) == null ||StringUtils.isBlank(kv.get(key).getValue())  ) {
            return defaultvalue;
        }
        return Short.parseShort(kv.get(key).getValue());
    }
    public int getInt(String section, String key, int defaultvalue) {
        Object value = entries.get(section);
        if (value instanceof Map) {
            return getInt((Map<String, ConfigMapper>) value, section, key, defaultvalue);
        }
        return defaultvalue;
    }
    public int getInt(Map<String, ConfigMapper> kv,String section, String key, int defaultvalue) {
        if (kv == null || kv.get(key) == null ||StringUtils.isBlank(kv.get(key).getValue())  ) {
            return defaultvalue;
        }
        return Integer.parseInt(kv.get(key).getValue());
    }
    public long getLong(Map<String, ConfigMapper> kv,String section, String key, long defaultvalue) {
        if (kv == null || kv.get(key) == null ||StringUtils.isBlank(kv.get(key).getValue())  ) {
            return defaultvalue;
        }
        return Long.parseLong(kv.get(key).getValue());
    }
    public long getLong(String section, String key, long defaultvalue) {
        Object value = entries.get(section);
        if (value instanceof Map) {
            return getLong((Map<String, ConfigMapper>) value, section, key, defaultvalue);
        }
        return defaultvalue;
    }


    public Map<String, ConfigMapper> beanToConfigMapper(Object obj){
        if (obj == null) {
            return null;
        }
        Map<String, ConfigMapper> connConfig = new LinkedHashMap<>();
        Class<?> aClass = obj.getClass();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(aClass);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                String key = property.getName();
                if (key.compareToIgnoreCase("class") == 0) {
                    continue;
                }
                Method getter = property.getReadMethod();
                Object value = null;
                try {
                    value = getter != null ? getter.invoke(obj) : null;
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
                connConfig.put(key, ConfigMapper.builder().value(StringUtils.defaultNullToString(value)).build());
            }
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        return connConfig;
    }

    public  <T> T configMapperToBean(String key, Map<String, ConfigMapper> configMapper,Class<T> clazz){
        try {
            T t = clazz.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                try {
                    Method setter = property.getWriteMethod();
                    if (setter != null) {
                        String field = property.getName();
                        Class<?> propertyType = property.getPropertyType();
                        String name = propertyType.getName();
                        String value = getString(configMapper, key, field, null);
                        if (value != null) {
                            if (propertyType.equals(Integer.class)|| StringUtils.equalsIgnoreCase(name,"int")) {
                                setter.invoke(t, Integer.valueOf(value));
                            } else if (propertyType.equals(Double.class)|| StringUtils.equalsIgnoreCase(name,"double")) {
                                setter.invoke(t, Double.valueOf(value));
                            } else if (propertyType.equals(Long.class)|| StringUtils.equalsIgnoreCase(name,"long")) {
                                setter.invoke(t, Long.valueOf(value));
                            } else if (propertyType.equals(Float.class)|| StringUtils.equalsIgnoreCase(name,"float")) {
                                setter.invoke(t, Float.valueOf(value));
                            } else if (propertyType.equals(Short.class) || StringUtils.equalsIgnoreCase(name,"short")) {
                                setter.invoke(t, Short.valueOf(value));
                            }  else if (propertyType.equals(String.class)) {
                                setter.invoke(t, value);
                            } else if (propertyType.equals(Boolean.class) || StringUtils.equalsIgnoreCase(name,"boolean")) {
                                setter.invoke(t, Boolean.valueOf(value));
                            }else {
                                setter.invoke(t, value);
                            }
                        }
                    }
                }catch (InvocationTargetException e ){
                    e.printStackTrace();
                }
            }
            return t;
        } catch (InstantiationException | IllegalAccessException | IntrospectionException e) {
            e.printStackTrace();
        }
        return null;
    }


    public   String byteToStr(byte[] b){
        if(b== null){
            return null;
        }
        String character =  getString(ConfigConstant.CharacterEncoding.SECTION, ConfigConstant.CharacterEncoding.CHARACTER, PublicConstant.CharacterEncoding.UTF_8);
        try {
            return new String(b,character);
        } catch (UnsupportedEncodingException e) {
            log.error("不支持的编码格式",e);
        }
        return null;
    }

    public   byte[] strToByte(String s){
        if(StringUtils.isNotBlank(s)){
            String character =  getString(ConfigConstant.CharacterEncoding.SECTION, ConfigConstant.CharacterEncoding.CHARACTER, PublicConstant.CharacterEncoding.UTF_8);
            try {
                return s.getBytes(character);
            } catch (UnsupportedEncodingException e) {
                log.error("不支持的编码格式",e);
            }
        }
        return null;
    }

}
